package com.github.cyf.upload.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import sun.misc.Cleaner;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.MappedByteBuffer;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessController;
import java.security.PrivilegedAction;

@Slf4j
public class FileUtil  {

    public static final String DOT = ".";


    public static String getRelativePath(String dir, String filePath) {
        if (StringUtils.isBlank(dir) || StringUtils.isBlank(filePath)) {
            throw new IllegalArgumentException("dir or filePath is blank");
        }
        Path basePath = Paths.get(dir);
        Path path = Paths.get(filePath);
        if (!path.startsWith(basePath)) {
            throw new IllegalArgumentException("filePath is not under dir");
        }
        String relativePath = basePath.relativize(path).toString();
        relativePath = relativePath.replace("\\+", "/");
        if (!relativePath.startsWith("/")) {
            relativePath = "/" + relativePath;
        }
        return relativePath;
    }

    public static void close(FileInputStream in, MappedByteBuffer byteBuffer) {

        if (null != in) {
            try {
                in.getChannel().close();
                in.close();
            } catch (IOException e) {
                log.error("close error:"+e.getMessage(), e);
            }
        }
        if (null != byteBuffer) {
            freedMappedByteBuffer(byteBuffer);
        }
    }

    /**
     * 在MappedByteBuffer释放后再对它进行读操作的话就会引发jvm crash，在并发情况下很容易发生 正在释放时另一个线程正开始读取，于是crash就发生了。所以为了系统稳定性释放前一般需要检 查是否还有线程在读或写
     */
    public static void freedMappedByteBuffer(final MappedByteBuffer mappedByteBuffer) {

        try {
            if (mappedByteBuffer == null) {
                return;
            }

            mappedByteBuffer.force();
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {

                    try {
                        Method getCleanerMethod = mappedByteBuffer.getClass().getMethod("cleaner", new Class[0]);
                        getCleanerMethod.setAccessible(true);
                        Cleaner cleaner = (Cleaner) getCleanerMethod.invoke(mappedByteBuffer,
                                new Object[0]);
                        cleaner.clean();
                    } catch (Exception e) {
                        log.error("clean MappedByteBuffer error!!!", e);
                    }
                    log.info("clean MappedByteBuffer completed!!!");
                    return null;
                }
            });

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
